﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Magiq.Delete;
using Magiq.Insert;
using Magiq.Tests.Support;
using Magiq.Update;
using NUnit.Framework;
using Rhino.Mocks;
using Rhino.Mocks.Constraints;

namespace Magiq.Tests {
    [TestFixture]
    public class ConfigurationTests {
        #region Setup/Teardown

        [SetUp]
        public void SetUp() {
            Magiq.Clear();
            items = new TestQueryable(new List<Model>
                                          {
                                              new Model("a"),
                                              new Model("b"),
                                              new Model("c")
                                          });
        }

        #endregion

        private TestQueryable items;

        private static IMagiqProvider RegisterMockMagiq() {
            var magiq = MockRepository.GenerateMock<IMagiqProvider>();
            magiq.Expect(x => x.GetUpdateProvider()).Return(MockRepository.GenerateMock<IUpdateProvider>()).Repeat.Any();
            magiq.Expect(x => x.GetInsertProvider()).Return(MockRepository.GenerateMock<IInsertProvider>()).Repeat.Any();
            magiq.Expect(x => x.GetDeleteProvider()).Return(MockRepository.GenerateMock<IDeleteProvider>()).Repeat.Any();
            Magiq.Register(magiq);
            return magiq;
        }

        private class Model {
            public Model(string prop1) {
                Prop1 = prop1;
            }

            public string Prop1 { get; set; }
        }

        private class TestQueryable : IQueryable<Model> {
            private readonly List<Model> list;

            public TestQueryable(List<Model> list) {
                this.list = list;
            }

            public bool Enumerated { get; private set; }

            #region IQueryable<Model> Members

            public IEnumerator<Model> GetEnumerator() {
                Enumerated = true;
                return list.GetEnumerator();
            }

            IEnumerator IEnumerable.GetEnumerator() {
                return GetEnumerator();
            }

            public Expression Expression {
                get { throw new NotImplementedException(); }
            }

            public Type ElementType {
                get { throw new NotImplementedException(); }
            }

            public IQueryProvider Provider {
                get { throw new NotImplementedException(); }
            }

            #endregion
        }

        [Test]
        public void ShouldCallMagiqToObjectsIfNoRegisteredMagiqHandlesTheQueryable() {
            var magiq = RegisterMockMagiq();
            magiq.Expect(x => x.Handles(null)).IgnoreArguments().Return(false).Repeat.Any();

            items.Set(x => x.Prop1, "lala").Update();
            items.Enumerated.Should(Be.True);
        }

        [Test]
        public void ShouldNotUseMagiqsThatDoesntHandlesTheQueryable() {
            var magiq = RegisterMockMagiq();
            magiq.Expect(x => x.Handles(null)).IgnoreArguments().Return(false).Repeat.Any();
            var magiq2 = RegisterMockMagiq();
            magiq2.Expect(x => x.Handles(null)).Constraints(Is.TypeOf<TestQueryable>()).Return(true).Repeat.Once();
            magiq2.GetUpdateProvider().Expect(x => x.Execute<Model>(null)).IgnoreArguments()
                .Callback((IQueryableUpdate<Model> x) => x.Where is TestQueryable).Return(2).Repeat.Once();

            items.Set(x => x.Prop1, "lala").Update().Should(Be.EqualTo(2));
        }

        [Test]
        public void ShouldUseConfiguredMagiqs() {
            var magiq = RegisterMockMagiq();
            magiq.Expect(x => x.Handles(null)).Constraints(Is.TypeOf<TestQueryable>()).Return(true).Repeat.Once();
            magiq.GetUpdateProvider().Expect(x => x.Execute<Model>(null)).IgnoreArguments()
                .Callback((IQueryableUpdate<Model> x) => x.Where is TestQueryable).Return(2).Repeat.Once();

            items.Set(x => x.Prop1, "lala").Update().Should(Be.EqualTo(2));
        }
    }
}